package com.format.Service;
import com.format.Public.MusicFormat;
import com.format.Utils.DataUtils;
import com.format.Utils.ScaleTran;
import org.json.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
@Service
public class NCMService implements MusicService{
    @Override
    public void decode(MultipartFile file, HttpServletResponse response) throws Exception{
        InputStream inputStream = file.getInputStream();
        byte[] buffer = new byte[1024];
        byte[] core_key = ScaleTran.hex("687A4852416D736F356B496E62617857");
        byte[] meta_key = ScaleTran.hex("2331346C6A6B5F215C5D2630553C2728");
        inputStream.read(buffer, 0, 8);
        //校验NCM文件格式header
        String header = DataUtils.bytesToHexString(buffer,0,8);
        if (!"4354454e4644414d".equals(header)) {
            throw new RuntimeException("文件格式正确");
        }
        inputStream.skip(2);
        inputStream.read(buffer,0,4);
        //得到AES秘钥长度144
        int key_length = Byte.toUnsignedInt(buffer[0]);
        //先后读取 144字节
        inputStream.read(buffer,0,key_length);
        // ^= 0x64可能混淆策略
        for (int i = 0; i < key_length; i++) {
            buffer[i] ^= 0x64;
        }
        byte[] key_data = Arrays.copyOfRange(buffer, 0, key_length);
        // 创建AES加密器并初始化
        Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); // 指定加密算法、模式和填充方式
        SecretKeySpec secretKey = new SecretKeySpec(core_key, "AES"); // 使用字节数组创建密钥对象
        cipher.init(Cipher.DECRYPT_MODE, secretKey); // 初始化为加密模式
        //解密
        byte[] decryptedBytes = cipher.doFinal(key_data);
        byte[] key_handle_data = Arrays.copyOfRange(unpad(decryptedBytes),17,unpad(decryptedBytes).length);
        // TODO 初始化key_box
        byte[] keyBox = new byte[256];
        for(int i = 0; i < 256; i++) {
            keyBox[i] = (byte)i;
        }

        int c = 0;
        int lastByte = 0;
        int keyOffset = 0;

        for(int i = 0; i < 256; i++) {

            byte swap = keyBox[i];

            c = (swap + lastByte + key_handle_data[keyOffset]) & 0xff;

            keyOffset++;
            if(keyOffset >= key_handle_data.length) {
                keyOffset = 0;
            }

            keyBox[i] = keyBox[c];
            keyBox[c] = swap;

            lastByte = c;
        }


        inputStream.read(buffer,0,4);
        ByteBuffer wrap = ByteBuffer.wrap(Arrays.copyOfRange(buffer, 0, 4));
        wrap.order(ByteOrder.LITTLE_ENDIAN);
        int meta_length = wrap.getInt();
        inputStream.read(buffer,0,meta_length);
        byte[] meta_data_array = Arrays.copyOfRange(buffer, 0, meta_length);
        for (int i = 0; i < meta_data_array.length; i++) {
            meta_data_array[i] ^= 0x63;
        }
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] meta_data = decoder.decode(Arrays.copyOfRange(meta_data_array, 22, meta_data_array.length));
        // 创建AES加密器并初始化
        Cipher cipher_meta = Cipher.getInstance("AES/ECB/NoPadding"); // 指定加密算法、模式和填充方式
        SecretKeySpec secretKey_meta = new SecretKeySpec(meta_key, "AES"); // 使用字节数组创建密钥对象
        cipher_meta.init(Cipher.DECRYPT_MODE, secretKey_meta); // 初始化为加密模式
        //解密
        byte[] decryptedBytes_meta = cipher_meta.doFinal(meta_data);
        byte[] meta_temp = unpad(decryptedBytes_meta);
        byte[] meta_res_data = Arrays.copyOfRange(meta_temp, 6, meta_temp.length);

        inputStream.read(buffer,0,4);
        ByteBuffer wrap1 = ByteBuffer.wrap(Arrays.copyOfRange(buffer, 0, 4));
        wrap1.order(ByteOrder.LITTLE_ENDIAN);
        int crc3 = wrap1.getInt();
        inputStream.skip(5);
        inputStream.read(buffer,0,4);
        ByteBuffer wrap2 = ByteBuffer.wrap(Arrays.copyOfRange(buffer, 0, 4));
        wrap2.order(ByteOrder.LITTLE_ENDIAN);
        int image_size = wrap2.getInt();
        inputStream.skip(image_size);
        JSONObject jsonObject = new JSONObject(new String(meta_res_data, StandardCharsets.UTF_8));
        String file_name = jsonObject.getString("musicName") + "." + jsonObject.getString("format");

        int byte_length = 0;
        byte[] chunk = new byte[0x8000];
        int bytesRead;
        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" +
                        URLEncoder.encode(file_name, StandardCharsets.UTF_8));
        while((bytesRead = inputStream.read(chunk)) != -1) {
            for(int i=1; i<=bytesRead; i++) {

                int j = i & 0xff;
                int handle_index = (keyBox[j] + keyBox[(keyBox[j] + j) & 0xff]);
                byte k = keyBox[handle_index & 0xff];
                chunk[i-1] ^= k;
            }
            response.getOutputStream().write(chunk, 0, bytesRead);
            byte_length += bytesRead;


        }
        //
        System.err.println("一共 " + "响应" + byte_length / (1024 * 1024) + "M");
        response.getOutputStream().flush();
    }

    @Override
    public MusicFormat getType() {
        return MusicFormat.NCM;
    }


    public static byte[] unpad(byte[] array) {
        int padding = array[array.length-1];
        if (padding < 0) {
            padding += 256;
        }
        //return Arrays.copyOfRange(array,17,array.length - padding);
        return  Arrays.copyOfRange(array,0,array.length - padding);
    }
}
